#!/usr/bin/perl -I../lib/perl -w

# $Id$

# Copyright KoLAPI (http://kol-api.googlecode.com)

use strict;
use KoL;
use KoL::CommandLine;
use KoL::FileUtils;
use KoL::Logging;
use KoL::TextUtils;
use KoL::Session;
use KoL::Item;
use KoL::Character;
use KoL::Terrarium;
use KoL::Inventory;
use KoL::Closet;
use KoL::Outfits;
use KoL::Wiki;
use KoL::Adventure;
use Data::Dumper;

my $kol = KoL->new();
my $cli = $kol->cli();
my $log = KoL::Logging->new();

$cli->addOption(
    'name'          => 'kol',
    'short-name'    => 'k',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL.'
);
$cli->addOption(
    'name'          => 'logging',
    'short-name'    => 'l',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::Logging.'
);
$cli->addOption(
    'name'          => 'command-line',
    'short-name'    => 'c',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::CommandLine.'
);
$cli->addOption(
    'name'          => 'text-utils',
    'short-name'    => 't',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::TextUtils.'
);
$cli->addOption(
    'name'          => 'file-utils',
    'short-name'    => 'f',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::FileUtils.'
);
$cli->addOption(
    'name'          => 'session',
    'short-name'    => 's',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::Session.'
);
$cli->addOption(
    'name'          => 'stats',
    'short-name'    => 'S',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::Stats.'
);
$cli->addOption(
    'name'          => 'character',
    #'short-name'    => 'C',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::Character.'
);
$cli->addOption(
    'name'          => 'terrarium',
    'short-name'    => 'T',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::Terrarium.'
);
$cli->addOption(
    'name'          => 'inventory',
    'short-name'    => 'i',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::Inventory.'
);
$cli->addOption(
    'name'          => 'items',
    'short-name'    => 'I',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::Item::*.'
);
$cli->addOption(
    'name'          => 'closet',
    'short-name'    => 'C',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::Closet.'
);
$cli->addOption(
    'name'          => 'wiki',
    'short-name'    => 'w',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::Wiki.'
);
$cli->addOption(
    'name'          => 'outfits',
    'short-name'    => 'o',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::Outfits.'
);
$cli->addOption(
    'name'          => 'adventure',
    'short-name'    => 'a',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Test KoL::Adventure.'
);
$cli->addOption(
    'name'          => 'test-all',
    'short-name'    => 'A',
    'type'          => 'boolean',
    'default'       => 0,
    'description'   => 'Run all tests..'
);
$cli->process();

my $mail = promptUser("Enter your email address:", 'string');
my $user = promptUser("Enter a KoL user name:", 'string');

testKoL() if ($cli->getValue('kol') || $cli->getValue('test-all'));
testLogging() if ($cli->getValue('logging') || $cli->getValue('test-all'));
testCommandLine() if ($cli->getValue('command-line') || $cli->getValue('test-all'));
testTextUtils() if ($cli->getValue('text-utils') || $cli->getValue('test-all'));
testFileUtils() if ($cli->getValue('file-utils') || $cli->getValue('test-all'));
testSession() if ($cli->getValue('session') || $cli->getValue('test-all'));
testItems() if ($cli->getValue('items') || $cli->getValue('test-all'));
testCharacter() if ($cli->getValue('character') || $cli->getValue('test-all'));
testTerrarium() if ($cli->getValue('terrarium') || $cli->getValue('test-all'));
testInventory() if ($cli->getValue('inventory') || $cli->getValue('test-all'));
testCloset() if ($cli->getValue('closet') || $cli->getValue('test-all'));
testOutfits() if ($cli->getValue('outfits') || $cli->getValue('test-all'));
testWiki() if ($cli->getValue('wiki') || $cli->getValue('test-all'));
testStats() if ($cli->getValue('stats') || $cli->getValue('test-all'));
testAdventure() if ($cli->getValue('adventure') || $cli->getValue('test-all'));

$log->msg("Testing complete.");

exit(0);

sub testKoL {
    my $kol = KoL->new();
    $log->msg("Testing KoL");
    
    $log->msg("Version: " . $kol->version());
    $log->msg("Server: " . $kol->host());
    $log->msg("Called from: " . calledFrom());
    $log->msg("You supplied: " . promptUser('Please enter a number: ', 'number'));
    $log->msg("You supplied: " . promptUser('Please enter a boolean: ', 'boolean'));
    $log->msg("You supplied: " . promptUser('Please enter a string: ', 'string'));
    $log->msg("You supplied: " . promptUser('Please press enter: ', 'string', 'The default value'));
    my $ans1 = promptUser('Please enter a secret string: ', 'password');
    my $ans2 = promptUser('Please enter the same secret string: ', 'password');
    if ($ans1 ne $ans2) {
        $log->msg("Your secret strings did not match!");
    } else {
        $log->msg("Your secret strings matched.");
    }
}

sub testLogging {
    $log->msg("Testing KoL::Logging");
    
    $log->msg("Logging object:");
    print Dumper($log);
    
    my $debug = $log->debugging();
    $log->setDebug(0);
    
    my $tmp = "This message is to test line wrapping.";
    while (length($tmp) < 1000) {$tmp .= " word";}
    $log->msg($tmp);
    
    $log->error("This is an error message.");
    $log->debug("This is a debug message that we don't see.");
    $log->setDebug(1);
    $log->debug("This is a debug message that we do see.");
    
    $log->msg("Test setting a bogus file handle.");
    $log->setDebugHandles([\*STDOUT, \*STDERR, 'foo']);
    $log->debug("We should see this debug message twice (STDOUT & STDERR).");
    
    $log->setDebug($debug);
    return;
}

sub testCommandLine {
    $log->msg("Testing KoL::CommandLine");
    
    # We are creating a fresh instance so we can test all the functionality.
    #   We pass the Logging object to keep the Logging CLI options from getting
    #   added into the option list since we are not processing the real
    #   command line at this point.
    my $cli = KoL::CommandLine->new('log', $log);
    my @argv = (
        '-i', '-i', '-i', '--int', '4',                         # 7
        '-I', '-I', '-I', '--multi-int', '4',                   # 1, 1, 1, 4
        '-s', 'val 1', '--str', 'val 2', '-s', 'val 3',         # 'val 3'
        '-S', 'val 1', '--multi-str', 'val 2', '-S', 'val 3',   # 'val 1', 'val 2', 'val 3'
        '-b', '-b', '--no-bool',                                # 0
        '-B', '-B', '--no-multi-bool',                          # 1, 1, 0
        '-H',                                                   # 1
    );
    
    my $callback = sub {
        my $cli = shift;
        my $opt = shift;
        my $val = shift;
        
        my $msg = "    CommandLine callback called for '$opt' with ";
        $msg .= "'$val'" if (ref($val) ne 'ARRAY');
        $msg .= "'" . join(', ', @{$val}) . "'" if (ref($val) eq 'ARRAY');
        $log->msg($msg);
        return;
    };
    
    $cli->addOption(
        'name'          => 'int',
        'short-name'    => 'i',
        'type'          => 'number',
        'multiple'      => 0,
        'description'   => 'A number option.',
        'callback'      => $callback,
    );
    $cli->addOption(
        'name'          => 'multi-int',
        'short-name'    => 'I',
        'type'          => 'number',
        'multiple'      => 1,
        'description'   => 'A multi-number option.',
        'callback'      => $callback,
    );
    $cli->addOption(
        'name'          => 'str',
        'short-name'    => 's',
        'type'          => 'string',
        'multiple'      => 0,
        'allowed'       => ['val 1', 'val 2', 'val 3'],
        'description'   => 'A string option.',
        'callback'      => $callback,
    );
    $cli->addOption(
        'name'          => 'multi-str',
        'short-name'    => 'S',
        'type'          => 'string',
        'multiple'      => 1,
        'allowed'       => ['val 1', 'val 2', 'val 3'],
        'description'   => 'A multi-string option.',
        'callback'      => $callback,
    );
    $cli->addOption(
        'name'          => 'bool',
        'short-name'    => 'b',
        'type'          => 'boolean',
        'multiple'      => 0,
        'description'   => 'A boolean option.',
        'callback'      => $callback,
    );
    $cli->addOption(
        'name'          => 'multi-bool',
        'short-name'    => 'B',
        'type'          => 'boolean',
        'multiple'      => 1,
        'description'   => 'A multi-boolean option.',
        'callback'      => $callback,
    );
    $cli->addOption(
        'name'          => 'hidden',
        'short-name'    => 'H',
        'type'          => 'boolean',
        'multiple'      => 0,
        #'description'   => 'A hidden option.',
        'callback'      => $callback,
    );
    $cli->addOption(
        'name'          => 'default',
        'short-name'    => 'D',
        'type'          => 'string',
        'multiple'      => 0,
        'default'       => 'Default Value',
        'description'   => 'A deafult option.',
        'callback'      => $callback,
    );
    $cli->addOption(
        'name'          => 'long-name-only',
        'type'          => 'string',
        'multiple'      => 0,
        'default'       => 'Another Default Value',
        'description'   => 'An option without a short name.',
        'callback'      => $callback,
    );
    
    $cli->printHelp("The help message with a preceeding message. Note: You " .
                    "should not see the --hidden option.");
    $log->msg("    Using argument list of: " . join(' ', @argv));
    $cli->process(\@argv);
    
    my @tests = (
        ['int', 7],
        ['multi-int', [1, 1, 1, 4]],
        ['str', 'val 3'],
        ['multi-str', ['val 1', 'val 2', 'val 3']],
        ['bool', 0],
        ['multi-bool', [1, 1, 0]],
        ['hidden', 1],
        ['default', 'Default Value'],
        ['long-name-only', 'Another Default Value'],
    );
    foreach my $test (@tests) {
        my ($error);
        my $opt = $test->[0];
        my $val = $cli->getValue($opt);
        my $test = $test->[1];
        
        $val = ref($val) eq 'ARRAY' ? join(', ', @{$val}) : $val;
        $test = ref($test) eq 'ARRAY' ? join(', ', @{$test}) : $test;
        if ($val eq $test) {
            $log->msg("    Value for '$opt' passed validation.");
        } else {
            $log->error("    Value for '$opt' failed validation. Expected '$test', got '$val'.");
        }
    }
    
    return;
}

sub testTextUtils {
    $log->msg("Testing KoL::TextUtils");
    
    my $text = KoL::TextUtils->new();
    $text->setColumns(8);
    
    my $test = "test\n line 1\n line 2\n line 3";
    my $str = $text->wrap('', ' ', 'test line 1 line 2 line 3');
    
    if ($str eq $test) {
        $log->msg("    wrap() test succeeded.");
    } else {
        $log->error("    wrap() test failed:");
        print STDERR "Results:\n$str\n";
        print STDERR "Test:\n$test\n";
    }
}

sub testFileUtils {
    $log->msg("Testing KoL::FileUtils");
    my $file = "/tmp/lib_test.$$.out";
    
    $log->msg("Testing writeFile() to '$file'");
    if (!writeFile($file, "  Test writing to $file.\n")) {
        $log->error("Unable to write to '$file': $@");
        exit(1);
    }
    
    $log->msg("Testing readFile() to scalar from '$file'.");
    my $tmp = readFile($file);
    print $tmp;

    $log->msg("Testing writeFile() to handle");
    open(TMP, '>', $file) or die "Unable to open '$file': $!\n";
    if (!writeFile(\*TMP, "  Test writing to file handle.\n")) {
        $log->error("Unable to write to file handle: $@");
        exit(1);
    }
    close(TMP);
    
    $log->msg("Testing readFile() to array from '$file'.");
    my @tmp = readFile($file);
    print Dumper(@tmp);
    
    $log->msg("Testing appendFile() to '$file'.");
    if (!appendFile($file, "  Testing append file to $file.\n")) {
        $log->error("Unable to append to '$file': $@");
        exit(1);
    }
    
    $log->msg("Testing readFile() from file handle.");
    open(TMP, $file);
    $tmp = readFile(\*TMP);
    close(TMP);
    print $tmp;
    
    return;
}

sub testSession {
    $log->msg("Testing KoL::Session");
    my $sess = KoL::Session->new(
        'agent'         => 'KoLAPI/lib_test',
        'timeout'       => 60, # seconds
        'email'         => $mail,
    );
    
    if ($sess->loggedIn()) {
        $log->error("Incorrectly reporting that we are already logged in...");
        exit(1);
    }
    
    # Test login
    if (!$sess->login($user)) {
        $log->error("Unable to login as '$user': $@");
        exit(1);
    }
    
    if (!$sess->loggedIn()) {
        $log->error("Incorrectly reporting that we are not logged in...");
        exit(1);
    }
    
    # Test logout
    if (!$sess->logout()) {
        $log->error("Unable to logout '$user': $@");
        exit(1);
    }
    
    if ($sess->loggedIn()) {
        $log->error("Incorrectly reporting that we are still logged in...");
        exit(1);
    }
    
    return;
}

sub testCharacter {
    $log->msg("Testing KoL::Character");
    my $sess = KoL::Session->new(
        'email'         => $mail,
    );
    my $char = KoL::Character->new('session' => $sess);
    
    if ($char->update() || $@ !~ m/You must be logged in to use this method/) {
        $log->error("Initial update request should have failed due to " .
                    "not being logged in yet!");
        $log->error("    $@");
        exit(1);
    }
    
    if (!$sess->login($user)) {
        $log->error("Unable to login as '$user': $@");
        exit(1);
    }
    
    if (!$char->update()) {
        $log->error("Unable to update character info: $@");
        $sess->logout();
        exit(1);
    }
    print Dumper($char->{'player'});
    print Dumper($char->{'character'});
    print Dumper($char->{'skills'});
    print Dumper($char->{'effects'});
    
    if (!$sess->logout()) {
        $log->error("Unable to logout '$user': $@");
        exit(1);
    }
    
    return;
}

sub testTerrarium {
    $log->msg("Testing KoL::Terrarium");
    my $sess = KoL::Session->new(
        'email'         => $mail,
    );
    my $ter = KoL::Terrarium->new('session' => $sess);
    
    if (!$sess->login($user)) {
        $log->error("Unable to login as '$user': $@");
        exit(1);
    }
    
    my $curr = $ter->currentFamiliar();
    if ($@) {
        $log->error("Unable to get current familiar: $@");
        $sess->logout();
        exit(1);
    }
    $log->msg("You do not have a current familiar.") if (!$curr);
    
    my $fams = $ter->allFamiliars();
    if (!$fams && $@) {
        $log->error("Unable to retrieve list of familiars: $@");
        $sess->logout();
        exit(1);
    }
    $log->msg("You have " . (0 + @{$fams}) . " familiars.");
    
    if (!$curr) {
        $log->msg("This series of tests require that you have a current " .
                    "familiar, it is equiped, and after unequiping " .
                    "it the first equipable item is lockable.");
    } else {
        $log->msg("We are going to work with " . $curr->name() . " the " .
                    $curr->weight() . "lb " . $curr->type());
        if ($curr->name() =~ m/changed by KoLAPI/) {
            $log->msg("Familiar name previously changed by this test script.");
        } else {
            if (!$ter->changeName($curr, $curr->name() . " changed by KoLAPI")) {
                $log->error("Unable to change familiar name: $@");
                $sess->logout();
                exit(1);
            }
            $log->msg("Name changed.");
        }
        
        if (!$curr->equip()) {
            $log->msg("Your current familiar isn't equiped. Can't test unequip().");
        } else {
            $log->msg("Your current familiar is equipped with a " .
                        $curr->equip()->name());
            if (!$ter->unequip($curr)) {
                $log->error("Unable to unequip familiar: $@");
                $sess->logout();
                exit(1);
            }
            $log->msg("Current familiar unequiped.");
        }
        
        my $eq = $ter->availableEquipment();
        if ($@) {
            $log->error("Unable to retrieve list of available equipment: $@");
            $sess->logout();
            exit(1);
        }
        $log->msg("You have " . (0 + @{$eq}) . " unused pieces of equipment.");
        
        if (@{$eq} == 0) {
            $log->msg("You do not have any available familiar equipment. " .
                        "Unable to test equip(), lock(), and unlock()!");
        } else {
            $log->msg("We are going to equip " . $curr->name() .
                        " with a " . $eq->[0]->name() . ".");
            if (!$ter->equip($curr, $eq->[0])) {
                $log->error("Unable to equip item: $@");
                $sess->logout();
                exit(1);
            }
            $log->msg("Item equiped.");
            
            if (!$ter->lock()) {
                $log->error("Unable to lock familiar equipment: $@");
                $sess->logout();
                exit(1);
            }
            $log->msg("Equipment locked.");
            
            if (!$ter->unlock()) {
                $log->error("Unable to unlock familiar equipment: $@");
                $sess->logout();
                exit(1);
            }
            $log->msg("Equipment unlocked.");
        }
        
        $log->msg("Putting " . $curr->name() . " away.");
        if (!$ter->putAway()) {
            $log->error("Unable to put familiar away: $@");
            $sess->logout();
            exit(1);
        }
        
        $log->msg("Taking " . $curr->name() . " with us.");
        if (!$ter->takeThisOne($curr)) {
            $log->error("Unable to take familiar: $@");
            $sess->logout();
            exit(1);
        }
    }
    
    if (!$sess->logout()) {
        $log->error("Unable to logout '$user': $@");
        exit(1);
    }
    
    return;
}

sub testItems {
    $log->msg("Testing KoL::Item::*");
    my $sess = KoL::Session->new(
        'email'         => $mail,
    );
    
    if (!$sess->login($user)) {
        $log->error("Unable to login as '$user': $@");
        exit(1);
    }
    
    my $controller = {'session' => $sess, 'log' => $log};
    
    my @tests = (
        {
            'type'      => 'Booze',
            'name'      => 'bottle of gin',
            'descid'    => '693995227'
        },
        {
            'type'      => 'Food',
            'name'      => 'lemon',
            'descid'    => '286050768'
        },
        {
            'type'      => 'Usable',
            'name'      => 'hot wad',
            'descid'    => '802361695'
        },
        {
            'type'      => 'Hat',
            'name'      => 'wreath of laurels',
            'descid'    => '691085302'
        },
        {
            'type'      => 'Weapon',
            'name'      => 'Underworld flail',
            'descid'    => '229893442'
        },
        {
            'type'      => 'Pants',
            'name'      => 'square sponge pants',
            'descid'    => '714193120'
        },
        {
            'type'      => 'Accessory',
            'name'      => 'pirate fledges',
            'descid'    => '642596336'
        },
        {
            'type'      => 'Accessory',
            'name'      => 'continuum transfunctioner',
            'descid'    => '488592577'
        },
        {
            'type'      => 'FamiliarEquipment',
            'name'      => 'little bitty bathysphere',
            'descid'    => '149442346'
        },
        {
            'type'      => 'Shirt',
            'name'      => 'sea salt scrubs',
            'descid'    => '501620697'
        },
        {
            'type'      => 'Misc',
            'name'      => 'guitar pick',
            'descid'    => '376472670'
        },
        {
            'type'      => 'Potion',
            'name'      => 'goofballs',
            'descid'    => '185274556'
        },
        {
            'type'      => 'CombatItem',
            'name'      => 'disease',
            'descid'    => '482996145'
        },
    );
    $log->msg("Testing by descid lookup.");
    foreach my $test (@tests) {
        $log->msg("Looking up " . $test->{'name'});
        my $obj = KoL::Item->new(
            'controller'    => $controller,
            'descid'        => $test->{'descid'}
        );
        if (!$obj) {
            $log->error("Unable to lookup item: $@");
            $sess->logout();
            exit(1);
        }
        if (ref($obj) ne "KoL::Item::" . $test->{'type'}) {
            $log->error("Item returned is of type '" . ref($obj) . 
                        "' rather than 'Kol::Item::" . $test->{'type'} . "'.");
            $sess->logout();
            exit(1);
        }
        $log->msg("    Found '" . $obj->name() . "'.");
    }
    
    $log->msg("Testing by name lookup.");
    foreach my $test (@tests) {
        $log->msg("Looking up " . $test->{'name'});
        my $obj = KoL::Item->new(
            'controller'    => $controller,
            'name'          => $test->{'name'}
        );
        if (!$obj) {
            $log->error("Unable to lookup item: $@");
            $sess->logout();
            exit(1);
        }
        if (ref($obj) ne "KoL::Item::" . $test->{'type'}) {
            $log->error("Item returned is of type '" . ref($obj) . 
                        "' rather than 'Kol::Item::" . $test->{'type'} . "'.");
            $sess->logout();
            exit(1);
        }
        $log->msg("    Found '" . $obj->name() . "'.");
    }
    
    $sess->logout();
    
    return;
}

sub testInventory {
    $log->msg("Testing KoL::Inventory");
    my $sess = KoL::Session->new(
        'email'         => $mail,
    );
    my $inv = KoL::Inventory->new('session' => $sess);
    
    if (!$sess->login($user)) {
        $log->error("Unable to login as '$user': $@");
        exit(1);
    }
    
    foreach my $type qw(accessories booze combatItems food
                        hats miscellaneous pants potions shirts weapons) {
        $log->msg("    $type");
        my $x = $inv->$type();
        if (!$x && $@) {
            $log->error("Unable to get $type items: $@");
            $sess->logout();
            exit(1);
        } elsif (!keys(%{$x})) {
            $log->msg("You have no $type items.");
        } else {
            my (@list);
            foreach my $name (sort(keys(%{$x}))) {
                push(@list, "$name (" . $x->{$name}->count() . ")");
            }
            $log->msg("$type Items:\n" . join("\n", @list));
        }
    }
    
    my $equip = $inv->equipped();
    if ($@) {
        $log->error("Unable to get equipped items: $@");
        $sess->logout();
        exit(1);
    }
    if (!%{$equip}) {
        $log->msg("You have no equipped items.");
    } else {
        my $items = '';
        foreach my $name (sort(keys(%{$equip}))) {
            $items .= "\n" . $equip->{$name}->inUse() . " $name";
        }
        $log->msg("You have the following currently equipped:$items");
    }
    
    $log->msg("Testing selling items");
    my $items = $inv->allItems();
    foreach my $name (keys(%{$items})) {
        next if (!grep(/^\Q$name\E$/,
                    ("white snake skin", "white sword",
                    "white satin pants", "catgut")));
        
        my $count = int($items->{$name}->count() / 2) || 1;
        $log->msg("Selling $count out of " . $items->{$name}->count() . " of $name");
        my $meat = $items->{$name}->sell($count);
        if (!$meat) {
            $log->error("Unable to sell item: $@");
            $sess->logout();
            exit(1);
        }
        $log->msg("Sold $count for $meat meat.");
        $log->msg("We now have " . $items->{$name}->count() . " left.");
        last;
    }
    
    $log->msg("Testing discarding items");
    $items = $inv->allItems();
    foreach my $name (keys(%{$items})) {
        next if (!grep(/^\Q$name\E$/,
                    ("white snake skin", "white sword",
                    "white satin pants", "catgut")));
        
        $log->msg("Discarding 1 out of " . $items->{$name}->count() . " of $name");
        if (!$items->{$name}->discard()) {
            $log->error("Unable to discard item: $@");
            $sess->logout();
            exit(1);
        }
        $log->msg("We now have " . $items->{$name}->count() . " left.");
        last;
    }
    
    $log->msg("Testing drinking items");
    my $booze = $inv->booze();
    foreach my $name (keys(%{$booze})) {
        next if (!grep(/^\Q$name\E$/, ("can of Swiller")));
        
        $log->msg("Drinking 2 out of " . $booze->{$name}->count() . " of $name");
        my $results = $booze->{$name}->drink(2);
        if (!$results) {
            $log->error("Unable to drink item: $@");
            $sess->logout();
            exit(1);
        }
        $log->msg("We now have " . $booze->{$name}->count() . " left.");
        print Dumper($results);
        last;
    }
    
    if (!$sess->logout()) {
        $log->error("Unable to logout '$user': $@");
        exit(1);
    }
    
    return;
}

sub testCloset {
    $log->msg("Testing KoL::Closet");
    my $sess = KoL::Session->new(
        'email'         => $mail,
    );
    my $inv = KoL::Inventory->new('session' => $sess);
    my $clos = KoL::Closet->new('session' => $sess);
    
    if (!$sess->login($user)) {
        $log->error("Unable to login as '$user': $@");
        exit(1);
    }
    
    my $stats = $sess->stats();
    
    my $iItems = $inv->allItems();
    if ($@) {
        $log->error("Unable to get all inventory items: $@");
        $sess->logout();
        exit(1);
    }
    
    my (@items, @info);
    foreach my $name (keys(%{$iItems})) {
        last if (@items >= 2);
        next if ($iItems->{$name}->count() <= 1);
        push(@items, [$iItems->{$name}, 1]) if (@items);
        push(@items, $iItems->{$name}) if (!@items);
        push(@info, [$name, $iItems->{$name}->count()]);
    }
    
    if ($stats->meat() < 1) {
        $log->msg("You do not have enough meat, can not test putting meat in the closet.");
    } else {
        $log->msg("You have " . $stats->meat() . " meat and " .
                    $clos->meat() . " meat in your closet.");
        $log->msg("Testing putting 1 meat in closet.");
        if (!$clos->putMeat(1)) {
            $log->error("Unable to put meat in closet: $@");
            $sess->logout();
            exit(1);
        }
        $log->msg("You now have " . $stats->meat() . " meat.");
        
        $log->msg("Testing getting 1 meat from closet.");
        if (!$clos->takeMeat(1)) {
            $log->error("Unable to put meat in closet: $@");
            $sess->logout();
            exit(1);
        }
        $log->msg("You now have " . $stats->meat() . " meat.");
    }
    
    if (@items != 2) {
        $log->msg("You do not have enough items with a count greater " .
                    "than 1 that can be placed in the closet. Unable " .
                    "to test putting items in the closet and removing them.");
    } else {
        $log->msg("Testing putting items in closet.");
        my $msg = '';
        foreach my $item (@info) {
            $msg .= $item->[0] . " (" . $item->[1] . ")\n";
        }
        $log->msg("Working with:\n$msg");
        
        if (!$clos->putItems(@items)) {
            $log->error("Unable to put items in closet: $@");
            $sess->logout();
            exit(1);
        }
        
        $log->msg("Getting updated item list from closet.");
        my $cItems = $clos->items();
        if ($@) {
            $log->error("Unable to get closet inventory: $@");
            $sess->logout();
            exit(1);
        }
        
        $log->msg("Closet now contains:");
        foreach my $name (keys(%{$cItems})) {
            $log->msg("    $name (" . $cItems->{$name}->count() . ")");
        }
        @items = (
            $cItems->{$info[0]->[0]},
            [$cItems->{$info[1]->[0]}, $info[1]->[1]],
        );
        $log->msg("Retrieving put items from closet.");
        if (!$clos->takeItems(@items)) {
            $log->error("Unable to take items from closet: $@");
            $sess->logout();
            exit(1);
        }
    }
    if (!$sess->logout()) {
        $log->error("Unable to logout '$user': $@");
        exit(1);
    }
    
    return;
}

sub testOutfits {
    $log->msg("Testing KoL::Outfits");
    my $sess = KoL::Session->new(
        'email'         => $mail,
    );
    my $out = KoL::Outfits->new('session' => $sess);
    
    if (!$sess->login($user)) {
        $log->error("Unable to login as '$user': $@");
        exit(1);
    }
    
    my $norm = $out->normal();
    if ($@) {
        $log->error("Unable to get normal outfits: $@");
        $sess->logout();
        exit(1);
    }
    if (!$norm) {
        $log->msg("You do not have any normal outfits.");
    } else {
        $log->msg("You have the following normal outfits:\n" .
                    join("\n", @{$norm}));
    }
    
    my $cust = $out->custom();
    if ($@) {
        $log->error("Unable to get custom outfits: $@");
        $sess->logout();
        exit(1);
    }
    if (!$cust) {
        $log->msg("You do not have any custom outfits.");
    } else {
        $log->msg("You have the following custom outfits:\n" .
                    join("\n", @{$cust}));
    }
    
    $log->msg("Saving outfit as 'KoLAPI Outfit'.");
    if (!$out->saveOutfit('KoLAPI Outfit')) {
        $log->error("Unable to save outfit: $@");
        $sess->logout();
        exit(1);
    }
    
    $log->msg("Switching to 'KoLAPI Outfit'.");
    if (!$out->wearCustom('KoLAPI Outfit')) {
        $log->error("Unable to switch to 'KoLAPI Outfit': $@");
        $sess->logout();
        exit(1);
    }
    
    $log->msg("Switching to previous outfit.");
    if (!$out->wearPrevious()) {
        $log->error("Unable to switch to previous outfit: $@");
        $sess->logout();
        exit(1);
    }
    
    $cust = $out->custom();
    if ($@) {
        $log->error("Unable to get custom outfits: $@");
        $sess->logout();
        exit(1);
    }
    if (!$cust) {
        $log->msg("You do not have any custom outfits.");
    } else {
        $log->msg("You have the following custom outfits:\n" .
                    join("\n", @{$cust}));
    }
    
    $log->msg("Deleting outfit as 'KoLAPI Outfit'.");
    if (!$out->deleteOutfit('KoLAPI Outfit')) {
        $log->error("Unable to delete outfit: $@");
        $sess->logout();
        exit(1);
    }
    
    $log->msg("Let's get naked!");
    if (!$out->unequipAll()) {
        $log->error("Unable to unequip all: $@");
        $sess->logout();
        exit(1);
    }
    if (!$out->wearPrevious()) {
        $log->error("Unable to put my clothes back on: $@");
        $sess->logout();
        exit(1);
    }
    
    if (!$sess->logout()) {
        $log->error("Unable to logout '$user': $@");
        exit(1);
    }
    
    return;
}

sub testWiki {
    $log->msg("Testing KoL::Wiki");
    my $wiki = KoL::Wiki->new(
        'email'         => $mail,
    );
    
    $log->msg("hot wad (item):");
    print Dumper($wiki->getPage("hot wad"));
    
    $log->msg("hot blooded (effect):");
    print Dumper($wiki->getPage("Hot Blooded"));
    
    return;
}

sub testStats {
    $log->msg("Testing KoL::Stats");
    my $sess = KoL::Session->new(
        'email'         => $mail,
    );
    
    if (!$sess->login($user)) {
        $log->error("Unable to login as '$user': $@");
        exit(1);
    }
    
    my $stats = $sess->stats();
    
    my $mus = join('/', $stats->muscle());
    my $mys = join('/', $stats->mysticality());
    my $mox = join('/', $stats->moxie());
    my $hp = join('/', $stats->hp());
    my $mp = join('/', $stats->mp());
    
    my $effs = $stats->effects();
    my $effects = '';
    foreach my $name (sort(keys(%{$effs}))) {
        $effects .= "\n    $name (" . $effs->{$name}{'count'} . ")";
    }
    
    $log->msg("Current Stats:" .
                "\nLevel: " . $stats->level() .
                "\nTitle: " . $stats->title() .
                "\nMeat:  " . $stats->meat() .
                "\nDrunkenness:  " . $stats->drunkenness() .
                "\nTurns Left: " . $stats->turns() .
                "\nLast Adventure: " . $stats->lastAdventure() .
                "\nMuscle: $mus" .
                "\nMysticality: $mys" .
                "\nMoxie: $mox" .
                "\nHP: $hp" .
                "\nMP: $mp" .
                "\nEffects:$effects");
    
    if (!$sess->logout()) {
        $log->error("Unable to logout '$user': $@");
        exit(1);
    }
    
    return;
}

sub testAdventure {
    $log->msg("Testing KoL::Adventure");
    my $sess = KoL::Session->new(
        'email'         => $mail,
    );
    
    if (!$sess->login($user)) {
        $log->error("Unable to login as '$user': $@");
        exit(1);
    }
    
    # Spooky Forest
    my $adv = KoL::Adventure->new('session' => $sess, 'area-id' => '15');
    for (my $i = 0 ; $i < 5 ; $i++) {
        my $res = $adv->adventure();
        if (!$res) {
            $log->error("Unable to adventure: $@");
            $sess->logout();
            exit(1);
        }
        print Dumper($sess->lastResponse()->content());
        print Dumper($res);
    }
    
    if (!$sess->logout()) {
        $log->error("Unable to logout '$user': $@");
        exit(1);
    }
    
    return;
}
